﻿using namespace std;

#include "stdafx.h"
#include <afxwin.h>
#include <afxext.h>
#include <afxdisp.h>
#include <afxdtctl.h>
#include <afxcmn.h>
#include <iostream>

#include "FontMaker_Cli.h"
#include "resource.h"

int CALLBACK EnumFontFamExProc(ENUMLOGFONTEX* lpelfe, NEWTEXTMETRICEX* lpntme, DWORD FontType, LPARAM lParam)
{return 100;}

static int UTF8ToUnicode(const char* input, WCHAR* output, int size)
{
	return MultiByteToWideChar(CP_UTF8, 0, input, -1, output, size);
}

BOOL is_fontface_exists() {
	CFont font;

	if (font.CreatePointFont(12, m_cmd_parser.fontface)) {
		LOGFONTW lf{};
		lf.lfCharSet = DEFAULT_CHARSET;
		wcscpy_s(lf.lfFaceName, min(LF_FACESIZE, m_cmd_parser.fontface.GetLength() + 1), m_cmd_parser.fontface.GetBuffer());
		int iRet = EnumFontFamiliesExW(GetWindowDC(GetConsoleWindow()), &lf, (FONTENUMPROC)EnumFontFamExProc, (LPARAM)0, 0);
		font.DeleteObject();
		return iRet == 100 ? TRUE : FALSE;
	}

	return FALSE;
}

void init_font()
{
	if (!is_fontface_exists()) {
		wcout << L"无法找到指定的字体文件：" << m_cmd_parser.fontface.GetString() << endl;
		exit(1);
	}

	ZeroMemory(&log_font, sizeof(log_font));
	lstrcpy(log_font.lfFaceName, m_cmd_parser.fontface);
	log_font.lfCharSet = DEFAULT_CHARSET;
	log_font.lfWeight = m_cmd_parser.font_weight;
	log_font.lfItalic = m_cmd_parser.font_itatlic;
	log_font.lfHeight = m_cmd_parser.font_size;

	if (m_hFont != NULL) {DeleteObject(m_hFont);}

	m_hFont = CreateFontIndirect(&log_font);
	m_bitfont.SetFont(m_hFont);
	m_bitfont.SetSize(m_cmd_parser.font_width, m_cmd_parser.font_height);
	m_bitfont.SetOffset(m_cmd_parser.font_offset_x, m_cmd_parser.font_offset_y);
}

BOOL set_charset(int resource_id)
{
	m_charset.Delete();

	HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(resource_id), L"CHARSET");
	if (!hRsrc) {return false;}

	HGLOBAL hGlobal = LoadResource(NULL, hRsrc);
	if (!hGlobal) {return false;}

	LPVOID pResourceData = LockResource(hGlobal);
	if (!pResourceData) {return false;}

	DWORD dwSize = SizeofResource(NULL, hRsrc);
	if (0 == dwSize) {return false;}

	FreeResource(hGlobal);

	m_charset.CreateFromResource((LPCTSTR) pResourceData, dwSize);

	return true;
}

CString dedup_customized_file(WCHAR *buffer, int size)
{
	int cnt;
	WCHAR ch;
	WCHAR *buf;
	CString text;

	text = CString(buffer);
	cnt = 0;
	buf = (WCHAR *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (size + 1) * sizeof(WCHAR));

	if (buf == NULL) {
		wcout << L"内存不足！" << endl;
		return L"";
	}

	for (int i = 0; i < size; i++) {
		ch = text.GetAt(i);

		if (ch >= 0x20 && ch <= 0x7f) {continue;}

		if (wcschr(buf, ch) == NULL) {
			buf[cnt] = ch;
			cnt++;
		}
	}

	text = CString(buf);
	HeapFree(GetProcessHeap(), 0, buf);

	return text;
}

BOOL set_customized_charset(CString filename)
{
	int len;
	UINT size;
	char *buffer;
	WCHAR *text;
	CString content;
	CFile cf;

	if (!cf.Open(filename, CFile::modeRead | CFile::shareDenyNone)) {
		wcout << L"无法打开文件！" << endl;
		return false;
	}

	size = (UINT) cf.GetLength();
	buffer = (char *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size + 1);

	if (buffer == NULL) {
		cf.Close();
		wcout << L"内存不足！" << endl;
		return false;
	}

	if (cf.Read(buffer, size) != size) {
		cf.Close();
		HeapFree(GetProcessHeap(), 0, buffer);
		wcout << L"读取文件时发生错误!" << endl;
		return false;
	}

	cf.Close();

	len = UTF8ToUnicode(buffer, NULL, 0) + 1;
	text = (WCHAR *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));

	if (text == NULL) {
		HeapFree(GetProcessHeap(), 0, buffer);
		wcout << L"内存不足！" << endl;
		return false;
	}

	len = UTF8ToUnicode(buffer, text, len);
	content = dedup_customized_file(text, len);
	len = content.GetLength();

	CFile customized_index_table;
	if (!customized_index_table.Open(customized_index_table_filename,
		CFile::modeCreate | CFile::modeReadWrite | CFile::shareDenyNone)) {
		wcout << L"创建目标文件 " << customized_index_table_filename.GetString() << L" 失败!" << endl;
		exit(1);
	}

	customized_index_table.Write(content, len * 2);
	customized_index_table.Close();

	m_charset.Delete();
	m_charset.Create(content);
	HeapFree(GetProcessHeap(), 0, text);

	return true;
}

BOOL make_file(CString output_filename)
{
	CFile file;

	if (!file.Open(output_filename,
		CFile::modeCreate | CFile::modeReadWrite | CFile::shareDenyNone)) {
		wcout << L"创建目标文件 " << output_filename.GetString() << L" 失败!" << endl;
		exit(1);
	}

	// scan - 0: 水平, 1: 垂直
	//  msb - 1: 高位在前, 0: 低位在前
	//  var - 0: 固定宽带, 1: 可变宽度
	maker.MakeBinFile(&m_bitfont, &m_charset, &file, m_cmd_parser.scan_mode, m_cmd_parser.byte_order, m_cmd_parser.char_width, m_cmd_parser.reverse_display);
	m_character_count += m_charset.GetCharCount();

	file.Close();

	wcout << L"保存文件 " << output_filename.GetString() << L" 成功" << endl;

	return true;
}

/*
*	合并的文件包括：
*		文件头 [24]：
*			[4] 文件标识符 - FMUX
*			[4] 文件大小
*			[1] 字体宽高
*			[2] 有效字符数量
*			[1] 是否包含 GB2312 索引表
*			[1] 扫描模式
*			[1] 字节顺序
*			[4] ASCII 字模起始地址
*			[4] GB2312 字模起始地址
*			[2] 保留字
*		GB2312 索引表
*		ASCII 字模数据
*		GB2312 字模数据
*/
BOOL combine_files() {
	CFile ascii_file, gb2312_file, customized_file, customized_index_table_file, combined_file;
	DWORD index_table_size, gb2312_data_size;
	LPVOID pResourceData = nullptr;

	if (m_cmd_parser.using_customized_cst) {
		if (!customized_file.Open(customized_output_filename,
			CFile::modeRead | CFile::shareDenyNone)) {
			wcout << customized_output_filename.GetString() << L" 文件不存在" << endl;
			exit(1);
		}

		if (!customized_index_table_file.Open(m_cmd_parser.input_customized_cst,
			CFile::modeRead | CFile::shareDenyNone)) {
			wcout << m_cmd_parser.input_customized_cst.GetString() << L" 文件不存在" << endl;
			exit(1);
		}

		gb2312_data_size = (DWORD) customized_file.GetLength();
		index_table_size = (DWORD) customized_index_table_file.GetLength();
	} else {
		if (!ascii_file.Open(ascii_output_filename,
			CFile::modeRead | CFile::shareDenyNone)) {
			wcout << ascii_output_filename.GetString() << L" 文件不存在" << endl;
			exit(1);
		}

		if (m_cmd_parser.using_customized_file) {
			if (!customized_file.Open(customized_output_filename,
				CFile::modeRead | CFile::shareDenyNone)) {
				wcout << customized_output_filename.GetString() << L" 文件不存在" << endl;
				exit(1);
			}

			if (!customized_index_table_file.Open(customized_index_table_filename,
				CFile::modeRead | CFile::shareDenyNone)) {
				wcout << customized_index_table_filename.GetString() << L" 文件不存在" << endl;
				exit(1);
			}

			gb2312_data_size = (DWORD) customized_file.GetLength();
			index_table_size = (DWORD) customized_index_table_file.GetLength();
		} else {
			if (!gb2312_file.Open(gb2312_output_filename,
				CFile::modeRead | CFile::shareDenyNone)) {
				wcout << gb2312_output_filename.GetString() << L" 文件不存在" << endl;
				exit(1);
			}

			HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(IDR_NEW_GB2312), L"CHARSET");
			if (!hRsrc) {return false;}

			HGLOBAL hGlobal = LoadResource(NULL, hRsrc);
			if (!hGlobal) {return false;}

			pResourceData = LockResource(hGlobal);
			if (!pResourceData) {return false;}

			index_table_size = SizeofResource(NULL, hRsrc);
			if (0 == index_table_size) {return false;}

			FreeResource(hGlobal);

			gb2312_data_size = (DWORD) gb2312_file.GetLength();
		}
	}

	if (!combined_file.Open(m_cmd_parser.output_file,
		CFile::modeCreate | CFile::modeReadWrite | CFile::shareDenyNone)) {
		wcout << L"创建目标文件 " << m_cmd_parser.output_file.GetString() << L" 失败!" << endl;
		exit(1);
	}

	PFL_Header header = (PFL_Header)malloc(sizeof(FL_Header));

	header->magic[0] = 'F';
	header->magic[1] = 'M';
	header->magic[2] = 'U';
	header->magic[3] = m_cmd_parser.using_customized_file || m_cmd_parser.using_customized_cst ? 'Y' : 'X';

	DWORD length = FL_Header_Size + // 文件头大小
				index_table_size + // 自定义或 GB2312 索引表大小
				gb2312_data_size; // 自定义或 GB2312 字符数据大小

	length += m_cmd_parser.using_customized_cst ? 0 : (DWORD) ascii_file.GetLength(); // ASCII 字符数据大小

	header->file_size = length;
	header->font_width = m_cmd_parser.font_width;
	header->font_height = m_cmd_parser.font_height;
	header->char_count = m_character_count;
	header->has_index_table = m_cmd_parser.has_index_table;
	header->scan_mode = m_cmd_parser.scan_mode;
	header->byte_order = m_cmd_parser.byte_order;
	header->ascii_start = FL_Header_Size + (m_cmd_parser.has_index_table ? index_table_size : 0);
	header->gb2312_start = m_cmd_parser.using_customized_cst ? FL_Header_Size + index_table_size : header->ascii_start + (DWORD) ascii_file.GetLength();
	header->reserved[0] = '\0';
	header->reserved[1] = '\0';

	combined_file.Write(&header->magic, sizeof(header->magic));
	combined_file.Write(&header->file_size, sizeof(header->file_size));
	combined_file.Write(&header->font_width, sizeof(header->font_width));
	combined_file.Write(&header->font_height, sizeof(header->font_height));
	combined_file.Write(&header->char_count, sizeof(header->char_count));
	combined_file.Write(&header->has_index_table, sizeof(header->has_index_table));
	combined_file.Write(&header->scan_mode, sizeof(header->scan_mode));
	combined_file.Write(&header->byte_order, sizeof(header->byte_order));
	combined_file.Write(&header->ascii_start, sizeof(header->ascii_start));
	combined_file.Write(&header->gb2312_start, sizeof(header->gb2312_start));
	combined_file.Write(&header->reserved, sizeof(header->reserved));

	free(header);

	char* buffer;
	DWORD file_length;

	if (m_cmd_parser.using_customized_file || m_cmd_parser.using_customized_cst) {
		buffer = new char[index_table_size + 1];
		buffer[index_table_size] = 0;
		customized_index_table_file.Read(buffer, index_table_size);
		combined_file.Write(buffer, index_table_size);
	} else {
		combined_file.Write(pResourceData, index_table_size);
	}

	if (!m_cmd_parser.using_customized_cst) {
		file_length = (DWORD) ascii_file.GetLength();
		buffer = new char[file_length + 1];
		buffer[file_length] = 0;
		ascii_file.Read(buffer, file_length);

		combined_file.Write(buffer, file_length);
	}

	buffer = new char[gb2312_data_size + 1];
	buffer[gb2312_data_size] = 0;
	if (m_cmd_parser.using_customized_file || m_cmd_parser.using_customized_cst) {
		customized_file.Read(buffer, gb2312_data_size);
	} else {
		gb2312_file.Read(buffer, gb2312_data_size);
	}

	combined_file.Write(buffer, gb2312_data_size);

	if (m_cmd_parser.using_customized_cst) {
		customized_file.Close();
		customized_index_table_file.Close();
	} else {
		if (m_cmd_parser.using_customized_file) {
			customized_file.Close();
			customized_index_table_file.Close();
		} else {
			gb2312_file.Close();
		}

		ascii_file.Close();
	}

	combined_file.Close();

	return true;
}

int main()
{
	wcout.imbue(locale("CHS"));

	BOOL output_file_generated = false;

	m_cmd_parser.parse();
	// m_cmd_parser.print_params();

	init_font();

	if (m_cmd_parser.using_customized_cst) {
		m_charset.LoadFromFile(m_cmd_parser.input_customized_cst);
		output_file_generated = make_file(customized_output_filename);
	} else {
		if (set_charset(IDR_ASCII)) {
			output_file_generated = make_file(ascii_output_filename);
		}

		if (m_cmd_parser.using_customized_file) {
			if (set_customized_charset(m_cmd_parser.input_customized_file)) {
				output_file_generated = make_file(customized_output_filename);
			}
		} else {
			if (set_charset(IDR_NEW_GB2312)) {
				output_file_generated &= make_file(gb2312_output_filename);
			}
		}
	}

	if (!output_file_generated) {
		wcout << L"创建字库文件失败" << endl;
		exit(1);
	}

	if (combine_files()) {
		wcout << L"创建字库文件 " << m_cmd_parser.output_file.GetString() << L" 成功" << endl;

		m_cmd_parser.print_params();
	}

	if (m_cmd_parser.using_customized_cst) {
		DeleteFile(customized_output_filename);
	} else {
		DeleteFile(ascii_output_filename);

		if (m_cmd_parser.using_customized_file) {
			DeleteFile(customized_index_table_filename);
			DeleteFile(customized_output_filename);
		} else {
			DeleteFile(gb2312_output_filename);
		}
	}

	return 0;
}